/**
 * Copyright (c) 2004-2006 Regents of the University of California.
 * See "license-prefuse.txt" for licensing terms.
 */
package visual.ontogrator.pkg2;

import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Iterator;

import java.util.List;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.layout.Layout;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.ControlAdapter;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Table;
import prefuse.render.AbstractShapeRenderer;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.render.PolygonRenderer;
import prefuse.render.Renderer;
import prefuse.render.ShapeRenderer;
import prefuse.util.ColorLib;
import prefuse.util.GraphicsLib;
import prefuse.util.PrefuseLib;
import prefuse.visual.AggregateItem;
import prefuse.visual.AggregateTable;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;




/**
 * Demo application showcasing the use of AggregateItems to
 * visualize groupings of nodes with in a graph visualization.
 * 
 * This class uses the AggregateLayout class to compute bounding
 * polygons for each aggregate and the AggregateDragControl to
 * enable drags of both nodes and node aggregates.
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class VisualNetwork extends Display implements java.io.Serializable {
  
    public static final String GRAPH = "graph";
    public static final String NODES = "graph.nodes";
    public static final String EDGES = "graph.edges";
    public static final String AGGR = "aggregates";
    public static final String LBL = "key";
   
    Graph g = new Graph();
    Table table = new Table();
    Object[] n;
  
    
    
    
    public VisualNetwork(ArrayList<Gene> agene,ArrayList<List<List<Integer>>> no, ArrayList<Integer> selectedItens, int[] palette) {
        // initialize display and data
        super(new Visualization());
        initDataGroups(agene, no, selectedItens);
        
        // set up the renderers
        // draw the nodes as basic shapes
        //Renderer nodeR = new ShapeRenderer(20);
        // Renderer nodeR = new LabelRenderer("keyword");
        LabelRenderer nodeR = new LabelRenderer(LBL);
        nodeR.setRoundedCorner(8, 8); // round the corners  
        
        nodeR.setRenderType(AbstractShapeRenderer.RENDER_TYPE_FILL);  
        nodeR.setHorizontalAlignment(Constants.CENTER);
        
        

        
        // draw aggregates as polygons with curved edges
        Renderer polyR = new PolygonRenderer(Constants.POLY_TYPE_CURVE);
        ((PolygonRenderer)polyR).setCurveSlack(0.15f);
        
        DefaultRendererFactory drf = new DefaultRendererFactory();
        drf.setDefaultRenderer(nodeR);
        drf.add("ingroup('aggregates')", polyR);
       
        
       // DefaultRendererFactory rf = new DefaultRendererFactory();
        drf.setDefaultEdgeRenderer(new EdgeRenderer(Constants.EDGE_TYPE_CURVE));
       // drf.add("INGROUP('data')", new LabelRenderer("forca"));
        
         m_vis.setRendererFactory(drf);
        
        // set up the visual operators
        // first set up all the color actions
        ColorAction nStroke = new ColorAction(NODES, VisualItem.STROKECOLOR);
        nStroke.setDefaultColor(ColorLib.gray(100));
        nStroke.add("_hover", ColorLib.gray(50));
        
        ColorAction nFill = new ColorAction(NODES, VisualItem.FILLCOLOR);
        nFill.setDefaultColor(ColorLib.gray(255));
        nFill.add("_hover", ColorLib.gray(200));
     
        
        ColorAction nEdges = new ColorAction(EDGES, VisualItem.STROKECOLOR);
        nEdges.setDefaultColor(ColorLib.gray(100));
        
        ColorAction aStroke = new ColorAction(AGGR, VisualItem.STROKECOLOR);
        aStroke.setDefaultColor(ColorLib.gray(200));
        aStroke.add("_hover", ColorLib.rgb(255,100,100));
        
        ColorAction aText = new ColorAction(NODES,VisualItem.TEXTCOLOR, ColorLib.gray(0));
       
       //cores dos grupos agregates
        
      /* int []palette = new int[] {
            
            ColorLib.rgba(255,200,200,150),
           ColorLib.rgba(200,255,200,150),
            ColorLib.rgba(255,255,200,150),
            ColorLib.rgba(200,200,255,150)
        };*/
        ColorAction aFill = new DataColorAction(AGGR, "id",
                Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
        
        // bundle the color actions
        ActionList colors = new ActionList();
        colors.add(nStroke);
        colors.add(nFill);
        colors.add(nEdges);
        colors.add(aStroke);
        colors.add(aFill);
        colors.add(aText);///DEFINE COR DO TEXTO
        
        // now create the main layout routine
        ActionList layout = new ActionList(Activity.INFINITY);
     
        
        layout.add(colors);
        layout.add(new ForceDirectedLayout(GRAPH, false));
        layout.add(new AggregateLayout(AGGR));
        layout.add(new RepaintAction());
        m_vis.putAction("layout", layout);
        
  
        
        // set up the display
        setSize(800,600);
        pan(250, 250);
        setHighQuality(true);
        addControlListener(new AggregateDragControl());
        addControlListener(new ZoomControl());
        addControlListener(new PanControl());
        
        // set things running
        m_vis.run("layout");
    }
    
     
    public void setXY(ArrayList<Gene> agene){
      
        for(int i=0;i<n.length;i++) {
          
           //setar posições x e y
            NodeItem node = (NodeItem) n[i];
            try{
             
               agene.get(i).setX(node.getX());
               agene.get(i).setY(node.getY());
            }catch(Exception e){
               
            }
            
         
         
        }
       
    }
    private void initDataGroups(ArrayList<Gene> agene,ArrayList<List<List<Integer>>> no, ArrayList<Integer> selectedItens) {
       
        Table table2 = new Table();
        table2.addColumn(LBL, String.class); 
        g = new Graph(table2, false);
       // System.out.println("o numero de genes eh "+ agene.size());
        
        //criar nós de acordo com o número de genes
        Node[] nos = new Node[agene.size()];
       
        
        //nomear nós
        for (int i = 0; i < agene.size(); i++) {
            nos[i] = g.addNode();
            try {
                nos[i].set(LBL, agene.get(i).getNome_Lista());
            } catch (Exception e) {
                System.out.println(e);
                nos[i].set(LBL, "Gene " + i);
              
                
            }

        }
        
        /*for(int i=0;i<agene.size();i++){
            g.addEdge(nos[0], nos[i]);
        }*/
        
       
        //criar array de agregates
       // AggregateItem[] agreg = new AggregateItem[no.size()];
        AggregateItem[] agreg = new AggregateItem[selectedItens.size()];
       
        // add visual data groups
        VisualGraph vg = m_vis.addGraph(GRAPH, g);
        m_vis.setInteractive(EDGES, null, false);
        m_vis.setValue(NODES, null, VisualItem.SHAPE,
                new Integer(Constants.SHAPE_ELLIPSE));
        
        AggregateTable at = m_vis.addAggregates(AGGR);
        at.addColumn(VisualItem.POLYGON, float[].class);
        at.addColumn("id", int.class);
        
        Iterator nodes = vg.nodes();
        n = new Object[agene.size()];
        
        
        //preenche vetor de nós
      
        // int tot = 20;
       // double grau = (double)360/(double)tot;
       
        int r = 0;
        while (nodes.hasNext()) {
          
            n[r] = nodes.next();
           
            //setar posições x e y
            NodeItem node = (NodeItem) n[r];
            try{
               node.setFixed(true);
               node.setX(agene.get(r).getX());
               node.setY(agene.get(r).getY());
            }catch(Exception e){
                node.setFixed(false);
            }
           // System.out.println("Posição do nó >> "+node.getX());
         
           r++;
        }
       
       
        
        
        //gera a rede comparando todos os genes com todos
        ArrayList<String> arestas = new ArrayList<>();
        
        //percorre as funções  selecionadas
       for(int a=0; a<selectedItens.size();a++){
            int i = selectedItens.get(a);
           // System.out.println("valor de i "+i);
       
        //percorrer todas as info
        //for (int i = 0; i < no.size(); i++) {
            
            //criar um agregate apra cada item selecionado
            agreg[a] = (AggregateItem)at.addItem();
            agreg[a].setInt("id", a);
           
            // System.out.println(i+" - "+gene.getArrayTitulo().get(i));
            
            //percorre os genes
            for (int j = 0; j < no.get(i).size(); j++) {

                ///   System.out.println("******************************");
                // System.out.println("Gene "+j);
                
                //adiciona gene ao aggreg da informação
                
                
                //escreve as informações
                for (int k = 0; k < no.get(i).get(j).size(); k++) {
                    int info = no.get(i).get(j).get(k);
                                      
                        //compara com todos os genes com a mesma função
                    for (int l = 0; l < no.get(i).size(); l++) {
                        if (!no.get(i).get(l).isEmpty()) {
                            //se tiver alguma função adicionar o gene a esse grupo
                            agreg[a].addItem((VisualItem) n[j]);
                            
                            if (no.get(i).get(l).contains(info)) {
                                //se os genes nao for o mesmo
                                if (l != j) {
                                   // System.out.println("no agreg Gene " + j + " - " + "Gene " + l);
                                    //g.addEdge(nos[0], nos[1]);
                                    
                                    //verificar se existem a arestas, para não adicionar denovo ("0-1" "1-0")
                                    String aux, aux2;
                                    aux = j+"";
                                    aux2 = l+"";
                                   
                                    if (!(arestas.contains(aux+aux2)) || !(arestas.contains(aux2+aux))) {
                                        g.addEdge(nos[j], nos[l]);
                                        
                                        //guardar aresta de ida e volta para evitar repetições
                                        arestas.add(aux+aux2);
                                        arestas.add(aux2+aux);
                                      
                                       // System.out.println("Aresta Gene " + j + " - " + "Gene " + l+" Adicionada");
                                    }

                                 
                                }
                            }
                        }
                    }


                }
                //System.out.println("******************************");


            }
            //System.out.println("------------------------------------");
        }


       
   
     
        
        
       
        
        
           ///exemplo de agregação
           /* AggregateItem aitem = (AggregateItem)at.addItem();
            aitem.setInt("id", 1);
            aitem.addItem((VisualItem)n[0]);
            aitem.addItem((VisualItem)n[1]);
            aitem.addItem((VisualItem)n[2]);
            AggregateItem aitem2 = (AggregateItem)at.addItem();
            aitem2.setInt("id", 2);
            aitem2.addItem((VisualItem)n[3]);
            aitem2.addItem((VisualItem)n[4]);
            aitem2.addItem((VisualItem)n[5]);
            
            AggregateItem aitem3 = (AggregateItem)at.addItem();
            aitem3.setInt("id", 3);
            aitem3.addItem((VisualItem)n[6]);
            aitem3.addItem((VisualItem)n[7]);
            aitem3.addItem((VisualItem)n[8]);
            
            AggregateItem aitem4 = (AggregateItem)at.addItem();
            aitem4.setInt("id", 4);
            aitem4.addItem((VisualItem)n[9]);
            aitem4.addItem((VisualItem)n[10]);
            aitem4.addItem((VisualItem)n[11]);*/
            
        
        
        // add nodes to aggregates
        // create an aggregate for each 3-clique of nodes
      //  Iterator nodes = vg.nodes();
        /*for ( int i=0; i<3; ++i ) {
            AggregateItem aitem = (AggregateItem)at.addItem();
            aitem.setInt("id", i);
            for ( int j=0; j<3; ++j ) {
                aitem.addItem((VisualItem)nodes.next());
            }
        }*/
            
                  
     /*   for ( int i=0; i<3; ++i ) {
            Node n1 = g.addNode();
            Node n2 = g.addNode();
            Node n3 = g.addNode();
           g.addEdge(nos[0], nos[1]);
           g.addEdge(nos[2], nos[3]);
           g.addEdge(nos[1], nos[2]);
            g.addEdge(n1, n3);
            g.addEdge(n2, n3);
           g.addEdge(n1, n2);
          //  g.addEdge(n1, n3);
           // g.addEdge(n2, n3);
            n1.set(LBL, i+"GENE");
            n2.set(LBL, i+"");
            n3.set(LBL, i+"");
            nos[0].set(LBL, i+"GENE");
            nos[1].set(LBL, i+"");
            nos[2].set(LBL, i+"");
            System.out.println(n1.get(LBL));
        }
       // Edge e1;
        //drf.add(new InGroupPredicate(EDGE_DECORATORS), new LabelRenderer(VisualItem.LABEL));
        
        g.addEdge(0, 3);
        g.addEdge(3, 6);
        g.addEdge(6, 0);
      
        
        
       */ 
        
    }
    
    public static void main(String[] argv) {
        JFrame frame = demo();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
    
    public static JFrame demo() {
        //AggregateDemo ad = new AggregateDemo();
        JFrame frame = new JFrame("p r e f u s e  |  a g g r e g a t e d");
       // frame.getContentPane().add(ad);
        frame.pack();
        return frame;
    }
    
} // end of class AggregateDemo
/**
 * Layout algorithm that computes a convex hull surrounding
 * aggregate items and saves it in the "_polygon" field.
 */
class AggregateLayout extends Layout {
    
    private int m_margin = 5; // convex hull pixel margin
    private double[] m_pts;   // buffer for computing convex hulls
    
    public AggregateLayout(String aggrGroup) {
        super(aggrGroup);
    }
    
    /**
     * @see edu.berkeley.guir.prefuse.action.Action#run(edu.berkeley.guir.prefuse.ItemRegistry, double)
     */
    public void run(double frac) {
        
        AggregateTable aggr = (AggregateTable)m_vis.getGroup(m_group);
        // do we have any  to process?
        int num = aggr.getTupleCount();
        if ( num == 0 ) return;
        
        // update buffers
        int maxsz = 0;
        for ( Iterator aggrs = aggr.tuples(); aggrs.hasNext();  )
            maxsz = Math.max(maxsz, 4*2*
                    ((AggregateItem)aggrs.next()).getAggregateSize());
        if ( m_pts == null || maxsz > m_pts.length ) {
            m_pts = new double[maxsz];
        }
        
        // compute and assign convex hull for each aggregate
        Iterator aggrs = m_vis.visibleItems(m_group);
        while ( aggrs.hasNext() ) {
            AggregateItem aitem = (AggregateItem)aggrs.next();

            int idx = 0;
            if ( aitem.getAggregateSize() == 0 ) continue;
            VisualItem item = null;
            Iterator iter = aitem.items();
            while ( iter.hasNext() ) {
                item = (VisualItem)iter.next();
                if ( item.isVisible() ) {
                    addPoint(m_pts, idx, item, m_margin);
                    idx += 2*4;
                }
            }
            // if no aggregates are visible, do nothing
            if ( idx == 0 ) continue;

            // compute convex hull
            double[] nhull = GraphicsLib.convexHull(m_pts, idx);
            
            // prepare viz attribute array
            float[]  fhull = (float[])aitem.get(VisualItem.POLYGON);
            if ( fhull == null || fhull.length < nhull.length )
                fhull = new float[nhull.length];
            else if ( fhull.length > nhull.length )
                fhull[nhull.length] = Float.NaN;
            
            // copy hull values
            for ( int j=0; j<nhull.length; j++ )
                fhull[j] = (float)nhull[j];
            aitem.set(VisualItem.POLYGON, fhull);
            aitem.setValidated(false); // force invalidation
        }
    }
    
    private static void addPoint(double[] pts, int idx, 
                                 VisualItem item, int growth)
    {
        Rectangle2D b = item.getBounds();
        double minX = (b.getMinX())-growth, minY = (b.getMinY())-growth;
        double maxX = (b.getMaxX())+growth, maxY = (b.getMaxY())+growth;
        pts[idx]   = minX; pts[idx+1] = minY;
        pts[idx+2] = minX; pts[idx+3] = maxY;
        pts[idx+4] = maxX; pts[idx+5] = minY;
        pts[idx+6] = maxX; pts[idx+7] = maxY;
    }
    
} // end of class AggregateLayout


/**
 * Interactive drag control that is "aggregate-aware"
 */
class AggregateDragControl extends ControlAdapter {

    private VisualItem activeItem;
    protected Point2D down = new Point2D.Double();
    protected Point2D temp = new Point2D.Double();
    protected boolean dragged;
    
    /**
     * Creates a new drag control that issues repaint requests as an item
     * is dragged.
     */
    public AggregateDragControl() {
    }
        
    /**
     * @see prefuse.controls.Control#itemEntered(prefuse.visual.VisualItem, java.awt.event.MouseEvent)
     */
    public void itemEntered(VisualItem item, MouseEvent e) {
        Display d = (Display)e.getSource();
        d.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        activeItem = item;
        if ( !(item instanceof AggregateItem) )
            setFixed(item, true);
    }
    
    /**
     * @see prefuse.controls.Control#itemExited(prefuse.visual.VisualItem, java.awt.event.MouseEvent)
     */
    public void itemExited(VisualItem item, MouseEvent e) {
        if ( activeItem == item ) {
            activeItem = null;
            setFixed(item, true);
        }
        Display d = (Display)e.getSource();
        d.setCursor(Cursor.getDefaultCursor());
    }
    
    /**
     * @see prefuse.controls.Control#itemPressed(prefuse.visual.VisualItem, java.awt.event.MouseEvent)
     */
    public void itemPressed(VisualItem item, MouseEvent e) {
        if (!SwingUtilities.isLeftMouseButton(e)) return;
        dragged = true;
        Display d = (Display)e.getComponent();
        d.getAbsoluteCoordinate(e.getPoint(), down);
        if ( item instanceof AggregateItem )
            setFixed(item, true);
    }
    
    /**
     * @see prefuse.controls.Control#itemReleased(prefuse.visual.VisualItem, java.awt.event.MouseEvent)
     */
    public void itemReleased(VisualItem item, MouseEvent e) {
        if (!SwingUtilities.isLeftMouseButton(e)) return;
        if ( dragged ) {
            activeItem = null;
            setFixed(item, true);
            dragged = true;
        }            
    }
    
    /**
     * @see prefuse.controls.Control#itemDragged(prefuse.visual.VisualItem, java.awt.event.MouseEvent)
     */
    public void itemDragged(VisualItem item, MouseEvent e) {
        if (!SwingUtilities.isLeftMouseButton(e)) return;
        dragged = true;
        Display d = (Display)e.getComponent();
        d.getAbsoluteCoordinate(e.getPoint(), temp);
        double dx = temp.getX()-down.getX();
        double dy = temp.getY()-down.getY();
        
        move(item, dx, dy);
        
        down.setLocation(temp);
    }

    protected static void setFixed(VisualItem item, boolean fixed) {
        if ( item instanceof AggregateItem ) {
            Iterator items = ((AggregateItem)item).items();
            while ( items.hasNext() ) {
                setFixed((VisualItem)items.next(), fixed);
            }
        } else {
            item.setFixed(fixed);
        }
    }
    
    protected static void move(VisualItem item, double dx, double dy) {
        if ( item instanceof AggregateItem ) {
            Iterator items = ((AggregateItem)item).items();
            while ( items.hasNext() ) {
                move((VisualItem)items.next(), dx, dy);
            }
        } else {
            double x = item.getX();
            double y = item.getY();
            item.setStartX(x);  item.setStartY(y);
            item.setX(x+dx);    item.setY(y+dy);
            item.setEndX(x+dx); item.setEndY(y+dy);
        }
    }
    
} // end of class AggregateDragControl